package com.dxl.common.utils;

import java.beans.BeanInfo;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.List;


/**
 * Bean工具类
 * @author 邓小林
 */
public final class BeanUtils {
	
	/**
	 * 获取字段列表，搜索父类
	 * @param clazz   类
	 * @return
	 * @throws Exception
	 */
	public static List<Field> listFields(Class<?> clazz) {
		List<Field> fields = new ArrayList<Field>();
		if (Utils.isNull(clazz)) {
			return fields;
		}
		Field[] fs = clazz.getDeclaredFields();
		if (Utils.isNotEmpty(fs)) {
			for (Field f : fs) {
				fields.add(f);
			}
		}
		Class<?> parent = clazz.getSuperclass();
		while (Utils.isNotNull(parent) && !parent.equals(Object.class)) {
			fs = parent.getDeclaredFields();
			if (Utils.isNotEmpty(fs)) {
				for (Field f : fs) {
					fields.add(f);
				}
			}
			parent = parent.getSuperclass();
		}
		return fields;
	}
	
	/**
	 * 获取方法，搜索父类
	 * @param clazz   类
	 * @param methodName   方法名
	 * @param types   参数类型列表
	 * @return
	 * @throws Exception
	 */
	public static Method getMethod(Class<?> clazz, String methodName, Class<?>... types) {
		Method method = null;
		if (Utils.isNull(clazz) || Utils.isBlank(methodName)) {
			return method;
		}
		try {
			method = clazz.getDeclaredMethod(methodName, types);
		} catch (Exception e) {
			// ignored
		}
		Class<?> parent = clazz.getSuperclass();
		while (Utils.isNull(method) && Utils.isNotNull(parent) && !parent.equals(Object.class)) {
			try {
				method = parent.getDeclaredMethod(methodName, types);
			} catch (Exception e) {
				// ignored
			}
			parent = parent.getSuperclass();
		}
		return method;
	}
	
	/**
	 * 判断是否是子类
	 * @param clazz       类
	 * @param parentClass 父类
	 * @return
	 */
	public static boolean isSubClassOf(Class<?> clazz, Class<?> parentClass) {
		Class<?> parent = clazz.getSuperclass();
		while (Utils.isNotNull(parent)) {
			if (parent.equals(parentClass)) {
				return true;
			}
			parent = parent.getSuperclass();
		}
		return false;
	}
	
	/**
	 * 获取非static、final字段列表，搜索父类
	 * @param clazz                类
	 * @param annotationClass      注解类
	 * @return
	 * @throws Exception
	 */
	public static List<Field> listNonStaticFinalFields(Class<?> clazz, Class<? extends Annotation> annotationClass) {
		List<Field> fields = new ArrayList<Field>();
		if (Utils.isNull(clazz)) {
			return fields;
		}
		Field[] fs = clazz.getDeclaredFields();
		if (Utils.isNotEmpty(fs)) {
			for (Field f : fs) {
				if (Modifier.isStatic(f.getModifiers()) || Modifier.isFinal(f.getModifiers()) || (Utils.isNotNull(annotationClass) && Utils.isNull(f.getAnnotation(annotationClass)))) {
					continue;
				}
				fields.add(f);
			}
		}
		Class<?> parent = clazz.getSuperclass();
		while (Utils.isNotNull(parent) && !parent.equals(Object.class)) {
			fs = parent.getDeclaredFields();
			if (Utils.isNotEmpty(fs)) {
				for (Field f : fs) {
					if (Modifier.isStatic(f.getModifiers()) || Modifier.isFinal(f.getModifiers()) || (Utils.isNotNull(annotationClass) && Utils.isNull(f.getAnnotation(annotationClass)))) {
						continue;
					}
					fields.add(f);
				}
			}
			parent = parent.getSuperclass();
		}
		return fields;
	}
	
	/**
	 * 获取方法上或者类上的注解，搜索父类
	 * @param method              方法
	 * @param annotationClass     注解类
	 * @return
	 * @throws Exception
	 */
	@SuppressWarnings({ "rawtypes", "unchecked" })
	public static Annotation getMethodOrClassAnnotation(Method method, Class annotationClass) {
		Annotation annotation = method.getAnnotation(annotationClass);
		if (Utils.isNull(annotation)) {
			Class<?> clazz = method.getDeclaringClass();
			annotation = clazz.getAnnotation(annotationClass);
			if (Utils.isNull(annotation)) {
				Class superclass = clazz.getSuperclass();
				while (Utils.isNull(annotation) && Utils.isNotNull(superclass)) {
					annotation = superclass.getAnnotation(annotationClass);
					superclass = superclass.getSuperclass();
				}
			}
		}
		return annotation;
	}
	
	/**
	 * 获取类上的注解，搜索父类
	 * @param clazz            类
	 * @param annotationClass  注解类
	 * @return
	 * @throws Exception
	 */
	@SuppressWarnings({ "rawtypes", "unchecked" })
	public static Annotation getClassAnnotation(Class clazz, Class annotationClass) {
		Annotation annotation = clazz.getAnnotation(annotationClass);
		if (Utils.isNull(annotation)) {
			Class superclass = clazz.getSuperclass();
			while (Utils.isNull(annotation) && Utils.isNotNull(superclass)) {
				annotation = superclass.getAnnotation(annotationClass);
				superclass = superclass.getSuperclass();
			}
		}
		return annotation;
	}
	
	/**
	 * 获取指定注解的字段名，搜索父类
	 * @param fieldNames       字段名列表
	 * @param clazz            类
	 * @param annotationClass  注解类
	 * @throws Exception
	 */
	@SuppressWarnings({ "rawtypes", "unchecked" })
	public static void listFieldNamesByAnnotation(List<String> fieldNames, Class<?> clazz, Class annotationClass) {
		Field[] fields = clazz.getDeclaredFields();
		
		if (Utils.isNotEmpty(fields)) {
			Annotation annotation;
			for (Field field : fields) {
				annotation = field.getAnnotation(annotationClass);
				if (annotation != null) {
					fieldNames.add(String.valueOf(field.getName()));
				}
			}
		}
		
		Class<?> superclass = clazz.getSuperclass();
		while (Utils.isNotNull(superclass)) {
			listFieldNamesByAnnotation(fieldNames, superclass, annotationClass);
			superclass = superclass.getSuperclass();
		}
	}
	
	/**
	 * 获取指定注解的字段名，搜索父类
	 * @param clazz            类
	 * @param annotationClass  注解类
	 * @return
	 * @throws Exception
	 */
	@SuppressWarnings("rawtypes")
	public static List<String> listFieldNamesByAnnotation(Class<?> clazz, Class annotationClass) {
		List<String> props = new ArrayList<String>();
		listFieldNamesByAnnotation(props, clazz, annotationClass);
		return props;
	}
	
	/**
	 * 获取getter方法
	 * @param fieldName   字段名
	 * @param type        字段类型
	 * @return
	 */
	public static String toGetterMethodName(String fieldName, Class<?> type) {
		if (Utils.isBlank(fieldName)) {
			return "";
		}
		boolean isBooleanType = boolean.class.equals(type);
		fieldName = fieldName.trim();
		int len = fieldName.length();
		return (isBooleanType ? "is" : "get") + Character.toUpperCase(fieldName.charAt(0)) + (len > 1 ? fieldName.substring(1) : "");
	}
	
	public static <T extends Object> void copyNonNullProperties(T source, T target) throws Exception {
		if (Utils.isNull(source) || Utils.isNull(target)) {
			return;
		}
		Class<?> clazz = source.getClass();
		BeanInfo beanInfo = Introspector.getBeanInfo(clazz);
		PropertyDescriptor[] propertyDescriptors = beanInfo.getPropertyDescriptors();
		Method rMethod, wMethod;
		Object o = null;
		for (PropertyDescriptor propertyDescriptor : propertyDescriptors) {
			rMethod = propertyDescriptor.getReadMethod();
			if (Utils.isNull(rMethod)) {
				continue;
			}
			o = rMethod.invoke(source);
			if (Utils.isNotNull(o)) {
				wMethod = propertyDescriptor.getWriteMethod();
				if (Utils.isNotNull(wMethod)) {
					wMethod.invoke(target, o);
				}
			}
		}
	}
}
